#include "Stack.h"
#include "Environment.h"

using namespace R;

void IntStack::push(uint64_t val) {
	rerror_on(top == STACK_SIZE, "stack overflow");
	stack[top++] = val;
}

uint64_t IntStack::pop(uint64_t n) {
	rerror_on(top < n, "stack underflow");
	return stack[top -= n];
}

uint64_t IntStack::peek(uint64_t n) {
	rerror_on(top <= n, "stack underflow");
	return stack[top - n - 1];
}

void IntStack::set(uint64_t idx, uint64_t val) {
	rerror_on(top <= idx, "stack underflow");
	stack[top - idx - 1] = val;
}

void CallStack::push(RModule *mod, uint64_t pc, EnvInfo* envIdx) {
  rerror_on(top == STACK_SIZE, "stack overflow");
  stack[top].set(mod, pc, envIdx);
  top ++;
}

ActivationRecord& CallStack::pop(uint64_t n) {
	rerror_on(top < n, "stack underflow");
	return stack[top -= n];
}

ActivationRecord* CallStack::peek(uint64_t n=0){
	rerror_on(top < n, "stack underflow");
	return &(stack[top - n]);
}

void AnyStack::swap() {
  Assert(top > 1, "Stack has not enough element to swap");
  std::swap(stackAny[top - 1], stackAny[top - 2]);
}

uint64_t AnyStack::size() {
  return top;
}

void AnyStack::dump() {
	R_VAR(Any*, val);
	for(int i=0; i<top; i++){
		val = peek(i);
		printf("[0x%.4x] ptr %p ====> ", i, val);
		val->print();
		PrintNewLine();
	}
}

void AnyStack::push(Any* x, bool safe) {
	R_PARAM(x);
	if(!safe) 
		rerror_on(top == STACK_SIZE, "Stack overflow.");
	stackAny[top++] = x;
}

// Push an argument in EVAL stack, even if is called in JIT
void AnyStack::JITPush(Any* x) {
	R_PARAM(x);
	push(x);
	jitddo(rprintf("Pushed argument ->");fflush(NULL);x->print();fflush(NULL);PrintNewLine();)
}

// Pop N values in EVAL stack, even if it is called in JIT
Any* AnyStack::JITPop(uint64_t n) {
	jitprintf("Pop "WORD" arguments", n);
	return pop(n);
}


Any* AnyStack::pop(uint64_t n) {
	rerror_on(top < n, "Stack underflow.");
	top -= n;
	return stackAny[top];
}

Any* AnyStack::JITPeek(uint64_t n) {
	return peek(n);
}

Any* AnyStack::peek(uint64_t n) {
	rerror_on(top <= n, "Stack underflow.");
	return stackAny[top - n - 1];
}

void AnyStack::set(uint64_t idx, Any* x) {
	R_PARAM(x);
	//assert(idx > 0 && idx < STACK_SIZE, "Set in stack of bound");
	stackAny[top-idx-1] = x;
}

EnvStack::EnvStack() {
	stack = new EnvInfo[STACK_SIZE];
	for(int i=0; i<STACK_SIZE; i++){
		stack[i].index = i;
		stack[i].stack = this;
	}
	top = 0;
}

EnvStack::~EnvStack() {
	delete [] stack;
}

EnvInfo* EnvStack::pushTopLevel(Environment* env){
	R_PARAM(env);
	rerror_on(top == STACK_SIZE, "stack overflow");
	stack[top].isGc = true;
	stack[top].env_gc = env;
	return &stack[top++];
}

EnvInfo* EnvStack::push(Environment* enclosingEnv, FunInfo* info, bool isGc) {
	R_PARAM(enclosingEnv);
	R_VAR(Environment*, the_env);
	rerror_on(top == STACK_SIZE, "stack overflow");
	if(isGc){
		the_env = Environment::doNew(enclosingEnv, info);
		stack[top].env_gc = the_env;
		stack[top].isGc = true;
	} else {
		nyi_fatal();
	}
	return &stack[top++];
}

EnvInfo* EnvStack::push(EnvInfo* parentInfo, FunInfo* info, bool isGc) {
	R_VAR(Environment*, the_env);
	rerror_on(top == STACK_SIZE, "stack overflow");
	uint64_t parent_envIdx = parentInfo->index;
	if(isGc){
		if(stack[parent_envIdx].isGc){
			the_env = Environment::doNew(stack[parent_envIdx].env_gc, info);
			stack[top].env_gc = the_env;
			stack[top].isGc = true;
		} else {
			stack[top].isGc = false;
			nyi_fatal();
		}
	} else {
		nyi_fatal();
	}
	return &stack[top++];
}

EnvInfo* EnvStack::pop(uint64_t n) {
	// FIXME with inlined env, have to check pointer to env.
	top -= n;
	return &stack[top];
}

EnvInfo* EnvStack::peek(uint64_t n){
	return &stack[top-1-n];
}
